/*
 * @(#) $Header$
 *
 * Copyright (C) 2011  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.baselib.util;

import java.util.Iterator;

/**
 * Class {@link Numeric} provides numerical algorithms similar to the C++ header
 * {@code <numeric>}.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.baselib.util.Numeric">Daniel Léonard</a>
 * @version $Revision$
 */
public class Numeric {

//---------------------------
// Constructor
//---------------------------

   /**
    * Let no one instanciate this class.
    */
   protected Numeric() {
   // do nothing
      }


//---------------------------
// accumulate()
//---------------------------

   /**
    * Adds all the numbers together.
    * @param   <N>   restriction on {@code Number}.
    * @param   numbers   the list of numbers.
    * @return   all the numbers added together.
    */
   public static <N extends Number> double accumulate(Iterator<N> numbers) {
      Iterable<N> iterable = Iterators.asIterable(numbers);
      double total = accumulate(iterable);
      return total;
      }

   /**
    * Adds all the numbers together starting at the initial value.
    * @param   <N>   restriction on {@code Number}.
    * @param   numbers   the list of numbers.
    * @param   init   the initial value.
    * @return   all the numbers added together.
    */
   public static <N extends Number> double accumulate(Iterator<N> numbers, double init) {
      Iterable<N> iterable = Iterators.asIterable(numbers);
      double total = accumulate(iterable, init);
      return total;
      }

   /**
    * Adds all the numbers together.
    * @param   <N>   restriction on {@code Number}.
    * @param   numbers   the list of numbers.
    * @return   all the numbers added together.
    */
   public static <N extends Number> double accumulate(Iterable<N> numbers) {
      double init = 0.0;
      double total = accumulate(numbers, init);
      return total;
      }

   /**
    * Adds all the numbers together starting at the initial value.
    * @param   <N>   restriction on {@code Number}.
    * @param   numbers   the list of numbers.
    * @param   init   the initial value.
    * @return   all the numbers added together.
    */
   public static <N extends Number> double accumulate(Iterable<N> numbers, double init) {
      double total = init;
      for (Number number : numbers) {
         double value = number.doubleValue();
         total += value;
         }
      return total;
      }

   /**
    * Adds all the numeric representation of the given elements together.
    * @param   <N>   restriction on {@code Number}.
    * @param   <E>   the type of elements.
    * @param   source   the list of element.
    * @param   transformer   from element to number.
    * @return   all the numeric representation added together.
    */
   public static <N extends Number, E> double accumulate(Iterator<E> source, UnaryFunction<N, E> transformer) {
      Iterable<E> iterable = Iterators.asIterable(source);
      double total = accumulate(iterable, transformer);
      return total;
      }

   /**
    * Adds all the numeric representation of the given elements together.
    * @param   <N>   restriction on {@code Number}.
    * @param   <E>   the type of elements.
    * @param   source   the list of element.
    * @param   transformer   from transformer to number.
    * @return   all the numeric representation added together.
    */
   public static <N extends Number, E> double accumulate(Iterable<E> source, UnaryFunction<N, E> transformer) {
      double init = 0.0;
      double total = accumulate(source, transformer, init);
      return total;
      }

   /**
    * Adds all the numeric representation of the given elements together
    * starting at the initial value.
    * @param   <N>   restriction on {@code Number}.
    * @param   <E>   the type of elements.
    * @param   source   the list of element.
    * @param   transformer   from element to number.
    * @param   init   the initial value.
    * @return   all the numeric representation added together.
    */
   public static <N extends Number, E> double accumulate(Iterator<E> source, UnaryFunction<N, E> transformer, double init) {
      Iterable<E> iterable = Iterators.asIterable(source);
      double total = accumulate(iterable, transformer, init);
      return total;
      }

   /**
    * Adds all the numeric representation of the given elements together
    * starting at the initial value.
    * @param   <N>   restriction on {@code Number}.
    * @param   <E>   the type of elements.
    * @param   source   the list of element.
    * @param   transformer   from element to number.
    * @param   init   the initial value.
    * @return   all the numeric representation added together.
    */
   public static <N extends Number, E> double accumulate(Iterable<E> source, UnaryFunction<N, E> transformer, double init) {
      double total = init;
      for (E e : source) {
         Number number = transformer.invoke(e);
         double value = number.doubleValue();
         total += value;
         }
      return total;
      }

   /**
    * Accumulates all the objects together starting with the given value.
    * @param   <E>   the type of the elements in the list of elements.
    * @param   <N>   the type of the intermediate values and the result of the
    *                accumulation.
    * @param   elements   the list of elements.
    * @param   init   the initial value.
    * @param   accumulator   the accumulation function.
    * @return   the accumulation.
    * @see   #accumulate(Iterable, Object, BinaryFunction)
    */
   public static <E, N> N accumulate(Iterator<E> elements, N init, BinaryFunction<N, N, E> accumulator) {
      Iterable<E> iterable = Iterators.asIterable(elements);
      N total = accumulate(iterable, init, accumulator);
      return total;
      }

   /**
    * Accumulates all the objects together starting with the given value.
    * @param   <E>   the type of the elements in the list of elements.
    * @param   <N>   the type of the intermediate values and the result of the
    *                accumulation.
    * @param   elements   the list of elements.
    * @param   init   the initial value.
    * @param   accumulator   the accumulation function.
    * @return   the accumulation.
    */
   public static <E, N> N accumulate(Iterable<E> elements, N init, BinaryFunction<N, N, E> accumulator) {
      N total = init;
      for (E element : elements) {
         total = accumulator.invoke(total, element);
         }
      return total;
      }

   }
